import os
import sys
import gtk
import itertools
from . import widgets


_accel_group = gtk.AccelGroup()


def action(path):
    def deco(func):
        func._gtdom_path = path
        return func
    return deco

class Module(object):
    TITLE = None
    PATH = "/"
    
    def __init__(self, rootconfig, owner):
        self.owner = owner
        self._keybindings = {}
        self._rootconfig = rootconfig
        self._config = self._getconf("/" + self.PATH)
    
    def _getconf(self, path):
        if path.startswith("/"):
            config = self._rootconfig
        else:
            config = self._config
        for p in path.split("/"):
            if p:
                config = config[p]
        return config
    
    def submodule(self, cls, *args, **kwargs):
        return cls(self._rootconfig, self, *args, **kwargs)
    
    def do_key_press_event(self, evt):
        key = (evt.keyval, evt.state)
        if key in self._keybindings:
            self._keybindings[key]()
            return True
        if self.owner:
            return self.owner.do_key_press_event(evt)
        return False
    
    def _get_actions(self):
        actions = []
        for name in dir(self):
            obj = getattr(self, name, None)
            path = getattr(obj, "_gtdom_path", None)
            if path is None:
                continue
            if not callable(obj):
                raise TypeError("action %r must be callable" % (name,))
            config = self._getconf(path)
            config["callback"] = obj
            actions.append(config)
        actions.sort(key = lambda config: config["order"])
        return actions
    
    def _get_title(self):
        if self.TITLE:
            title = self.TITLE
        else:
            title = self.__class__.__name__
            if title.lower().endswith("module"):
                title = title[:-6]
        return title
    
    def _render_actions_menu(self):
        actions = self._get_actions()
        if not actions:
            return None
        items = []
        def ignoreargs(f):
            def wrapper(*args):
                return f()
            return wrapper
        self._keybindings.clear()
        items = []
        groups = {}
        for config in actions:
            item = widgets.MenuItem(
                config["name"], 
                ignoreargs(config["callback"]), 
                keybinding = config["key"], 
                accel_group = _accel_group
            )
            for k in list(config["aliases"]) + [config["key"]]: 
                k = gtk.accelerator_parse(config["key"])
                self._keybindings[k] = config["callback"]
            if config["group"] not in groups:
                groups[config["group"]] = []
            groups[config["group"]].append(item)
        menus = [widgets.Menu(name, *items) 
            for name, items in sorted(groups.items())]
        return widgets.MenuBar(*menus)
    
    def _render_container(self, child):
        return widgets.Frame(self._get_title(), child)
    
    def _render(self, owner):
        raise NotImplementedError()
    
    def render(self, owner):
        self.owner = owner
        menu = self._render_actions_menu()
        body = self._render(self)
        if menu:
            box = gtk.VBox()
            box.pack_start(menu, expand = False, fill = False)
            box.pack_start(body)
            return self._render_container(box)
        else:
            return self._render_container(body)


class WindowedModule(Module):
    def on_close(self):
        pass
    
    def _realize(self, window):
        # XXX load size of all objects
        window.resize(887, 514)
    
    def _unrealize(self, window):
        # XXX save size of all objects
        print window.get_size()
    
    def _render_container(self, child):
        win = widgets.Window(self._get_title(), child)
        win.connect("destroy", lambda *args: self.on_close())
        win.connect("realize", self._realize)
        win.connect("unrealize", self._unrealize)
        win.show_all()
        return win


class Bunch(dict):
    def __repr__(self):
        if not self:
            return "%s()" % (self.__class__.__name__,)
        raw = "\n".join(sorted("%s = %r," % (k, v) for k, v in self.iteritems()))
        attrs = "\n    ".join(raw.splitlines())
        return "%s(\n    %s\n)" % (self.__class__.__name__, attrs)

class ActionInfo(Bunch):
    _actions_order = itertools.count()
    
    def __init__(self, name, group = "_Actions", key = None, aliases = (), 
            order = None):
        wellorder = self._actions_order.next()
        if order is None:
            order = wellorder
        Bunch.__init__(self, name = name, group = group, key = key, 
            aliases = aliases, order = order)

class Application(WindowedModule):
    def on_close(self):
        gtk.main_quit()
    
    @classmethod
    def _load_config(cls, filename):
        if filename is None:
            import __main__
            basename = __main__.__file__.rsplit(".", 1)[0]
            filename = basename + ".gtdom.py"
            if not os.path.exists(filename):
                return {}
        env = dict(Bunch = Bunch, ActionInfo = ActionInfo)
        execfile(filename, env)
        return env["config"]
    
    @classmethod
    def main(cls, args = (), kwargs = {}, argv = None, configfile = None):
        if argv is None:
            argv = sys.argv
        config = cls._load_config(configfile)
        inst = cls(config, None, *args, **kwargs)
        root = inst.render(None)
        gtk.main()













